/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                        Intel License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of Intel Corporation may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

#include "precomp.hpp"

#if defined WIN32 || defined WIN64 || defined _WIN64

#if _MSC_VER >= 1200
#pragma warning( disable: 4710 )
#endif

#include <commctrl.h>
#include <winuser.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>

static const char* trackbar_text =
	"                                                                                             ";

#if defined WIN64 || defined _WIN64

#define icvGetWindowLongPtr GetWindowLongPtr
#define icvSetWindowLongPtr( hwnd, id, ptr ) SetWindowLongPtr( hwnd, id, (LONG_PTR)(ptr) )
#define icvGetClassLongPtr  GetClassLongPtr

#define CV_USERDATA GWLP_USERDATA
#define CV_WNDPROC GWLP_WNDPROC
#define CV_HCURSOR GCLP_HCURSOR
#define CV_HBRBACKGROUND GCLP_HBRBACKGROUND

#else

#define icvGetWindowLongPtr GetWindowLong
#define icvSetWindowLongPtr( hwnd, id, ptr ) SetWindowLong( hwnd, id, (size_t)ptr )
#define icvGetClassLongPtr GetClassLong

#define CV_USERDATA GWL_USERDATA
#define CV_WNDPROC GWL_WNDPROC
#define CV_HCURSOR GCL_HCURSOR
#define CV_HBRBACKGROUND GCL_HBRBACKGROUND

#endif

void  FillBitmapInfo( BITMAPINFO* bmi, int width, int height, int bpp, int origin ) {
	assert( bmi && width >= 0 && height >= 0 && (bpp == 8 || bpp == 24 || bpp == 32));

	BITMAPINFOHEADER* bmih = &(bmi->bmiHeader);

	memset( bmih, 0, sizeof(*bmih));
	bmih->biSize = sizeof(BITMAPINFOHEADER);
	bmih->biWidth = width;
	bmih->biHeight = origin ? abs(height) : -abs(height);
	bmih->biPlanes = 1;
	bmih->biBitCount = (unsigned short)bpp;
	bmih->biCompression = BI_RGB;

	if ( bpp == 8 ) {
		RGBQUAD* palette = bmi->bmiColors;
		int i;
		for ( i = 0; i < 256; i++ ) {
			palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i;
			palette[i].rgbReserved = 0;
		}
	}
}

struct CvWindow;

typedef struct CvTrackbar {
	int signature;
	HWND hwnd;
	char* name;
	CvTrackbar* next;
	CvWindow* parent;
	HWND buddy;
	int* data;
	int pos;
	int maxval;
	void (*notify)(int);
	void (*notify2)(int, void*);
	void* userdata;
	int id;
}
CvTrackbar;


typedef struct CvWindow {
	int signature;
	HWND hwnd;
	char* name;
	CvWindow* prev;
	CvWindow* next;
	HWND frame;

	HDC dc;
	HGDIOBJ image;
	int last_key;
	int flags;
	int status;//0 normal, 1 fullscreen (YV)

	CvMouseCallback on_mouse;
	void* on_mouse_param;

	struct {
		HWND toolbar;
		int pos;
		int rows;
		WNDPROC toolBarProc;
		CvTrackbar* first;
	}
	toolbar;
}
CvWindow;


#define HG_BUDDY_WIDTH  130

#ifndef TBIF_SIZE
#define TBIF_SIZE  0x40
#endif

#ifndef TB_SETBUTTONINFO
#define TB_SETBUTTONINFO (WM_USER + 66)
#endif

#ifndef TBM_GETTOOLTIPS
#define TBM_GETTOOLTIPS  (WM_USER + 30)
#endif

static LRESULT CALLBACK HighGUIProc(  HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK WindowProc(  HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK MainWindowProc(  HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static void icvUpdateWindowPos( CvWindow* window );

static CvWindow* hg_windows = 0;
static CvWin32WindowCallback hg_on_preprocess = 0, hg_on_postprocess = 0;
static HINSTANCE hg_hinstance = 0;

static const char* highGUIclassName = "HighGUI class";
static const char* mainHighGUIclassName = "Main HighGUI class";

static void icvCleanupHighgui() {
	cvDestroyAllWindows();
	UnregisterClass(highGUIclassName, hg_hinstance);
	UnregisterClass(mainHighGUIclassName, hg_hinstance);
}

CV_IMPL int cvInitSystem( int, char** ) {
	static int wasInitialized = 0;

	// check initialization status
	if ( !wasInitialized ) {
		// Initialize the stogare
		hg_windows = 0;

		// Register the class
		WNDCLASS wndc;
		wndc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
		wndc.lpfnWndProc = WindowProc;
		wndc.cbClsExtra = 0;
		wndc.cbWndExtra = 0;
		wndc.hInstance = hg_hinstance;
		wndc.lpszClassName = highGUIclassName;
		wndc.lpszMenuName = highGUIclassName;
		wndc.hIcon = LoadIcon(0, IDI_APPLICATION);
		wndc.hCursor = (HCURSOR)LoadCursor(0, (LPSTR)(size_t)IDC_CROSS );
		wndc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);

		RegisterClass(&wndc);

		wndc.lpszClassName = mainHighGUIclassName;
		wndc.lpszMenuName = mainHighGUIclassName;
		wndc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
		wndc.lpfnWndProc = MainWindowProc;

		RegisterClass(&wndc);
		atexit( icvCleanupHighgui );

		wasInitialized = 1;
	}

	return 0;
}

CV_IMPL int cvStartWindowThread() {
	return 0;
}

static CvWindow* icvFindWindowByName( const char* name ) {
	CvWindow* window = hg_windows;

	for ( ; window != 0 && strcmp( name, window->name) != 0; window = window->next )
		;

	return window;
}


static CvWindow* icvWindowByHWND( HWND hwnd ) {
	CvWindow* window = (CvWindow*)icvGetWindowLongPtr( hwnd, CV_USERDATA );
	return window != 0 && hg_windows != 0 &&
		   window->signature == CV_WINDOW_MAGIC_VAL ? window : 0;
}


static CvTrackbar* icvTrackbarByHWND( HWND hwnd ) {
	CvTrackbar* trackbar = (CvTrackbar*)icvGetWindowLongPtr( hwnd, CV_USERDATA );
	return trackbar != 0 && trackbar->signature == CV_TRACKBAR_MAGIC_VAL &&
		   trackbar->hwnd == hwnd ? trackbar : 0;
}


static const char* icvWindowPosRootKey = "Software\\OpenCV\\HighGUI\\Windows\\";

// Window positions saving/loading added by Philip Gruebele.
//<a href="mailto:pgruebele@cox.net">pgruebele@cox.net</a>
// Restores the window position from the registry saved position.
static void
icvLoadWindowPos( const char* name, CvRect& rect ) {
	HKEY hkey;
	char szKey[1024];
	strcpy( szKey, icvWindowPosRootKey );
	strcat( szKey, name );

	rect.x = rect.y = CW_USEDEFAULT;
	rect.width = rect.height = 320;

	if ( RegOpenKeyEx(HKEY_CURRENT_USER, szKey, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS ) {
		// Yes we are installed.
		DWORD dwType = 0;
		DWORD dwSize = sizeof(int);

		RegQueryValueEx(hkey, "Left", NULL, &dwType, (BYTE*)&rect.x, &dwSize);
		RegQueryValueEx(hkey, "Top", NULL, &dwType, (BYTE*)&rect.y, &dwSize);
		RegQueryValueEx(hkey, "Width", NULL, &dwType, (BYTE*)&rect.width, &dwSize);
		RegQueryValueEx(hkey, "Height", NULL, &dwType, (BYTE*)&rect.height, &dwSize);

		if ( rect.x != (int)CW_USEDEFAULT && (rect.x < -200 || rect.x > 3000) ) {
			rect.x = 100;
		}
		if ( rect.y != (int)CW_USEDEFAULT && (rect.y < -200 || rect.y > 3000) ) {
			rect.y = 100;
		}

		if ( rect.width != (int)CW_USEDEFAULT && (rect.width < 0 || rect.width > 3000) ) {
			rect.width = 100;
		}
		if ( rect.height != (int)CW_USEDEFAULT && (rect.height < 0 || rect.height > 3000) ) {
			rect.height = 100;
		}

		RegCloseKey(hkey);
	}
}


// Window positions saving/loading added by Philip Gruebele.
//<a href="mailto:pgruebele@cox.net">pgruebele@cox.net</a>
// philipg.  Saves the window position in the registry
static void
icvSaveWindowPos( const char* name, CvRect rect ) {
	static const DWORD MAX_RECORD_COUNT = 100;
	HKEY hkey;
	char szKey[1024];
	char rootKey[1024];
	strcpy( szKey, icvWindowPosRootKey );
	strcat( szKey, name );

	if ( RegOpenKeyEx( HKEY_CURRENT_USER, szKey, 0, KEY_READ, &hkey) != ERROR_SUCCESS ) {
		HKEY hroot;
		DWORD count = 0;
		FILETIME oldestTime = { UINT_MAX, UINT_MAX };
		char oldestKey[1024];
		char currentKey[1024];

		strcpy( rootKey, icvWindowPosRootKey );
		rootKey[strlen(rootKey)-1] = '\0';
		if ( RegCreateKeyEx(HKEY_CURRENT_USER, rootKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ + KEY_WRITE, 0, &hroot, NULL) != ERROR_SUCCESS )
			//RegOpenKeyEx( HKEY_CURRENT_USER,rootKey,0,KEY_READ,&hroot) != ERROR_SUCCESS )
		{
			return;
		}

		for (;;) {
			DWORD csize = sizeof(currentKey);
			FILETIME accesstime = { 0, 0 };
			LONG code = RegEnumKeyEx( hroot, count, currentKey, &csize, NULL, NULL, NULL, &accesstime );
			if ( code != ERROR_SUCCESS && code != ERROR_MORE_DATA ) {
				break;
			}
			count++;
			if ( oldestTime.dwHighDateTime > accesstime.dwHighDateTime ||
					(oldestTime.dwHighDateTime == accesstime.dwHighDateTime &&
					 oldestTime.dwLowDateTime > accesstime.dwLowDateTime) ) {
				oldestTime = accesstime;
				strcpy( oldestKey, currentKey );
			}
		}

		if ( count >= MAX_RECORD_COUNT ) {
			RegDeleteKey( hroot, oldestKey );
		}
		RegCloseKey( hroot );

		if ( RegCreateKeyEx(HKEY_CURRENT_USER, szKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &hkey, NULL) != ERROR_SUCCESS ) {
			return;
		}
	} else {
		RegCloseKey( hkey );
		if ( RegOpenKeyEx( HKEY_CURRENT_USER, szKey, 0, KEY_WRITE, &hkey) != ERROR_SUCCESS ) {
			return;
		}
	}

	RegSetValueEx(hkey, "Left", 0, REG_DWORD, (BYTE*)&rect.x, sizeof(rect.x));
	RegSetValueEx(hkey, "Top", 0, REG_DWORD, (BYTE*)&rect.y, sizeof(rect.y));
	RegSetValueEx(hkey, "Width", 0, REG_DWORD, (BYTE*)&rect.width, sizeof(rect.width));
	RegSetValueEx(hkey, "Height", 0, REG_DWORD, (BYTE*)&rect.height, sizeof(rect.height));
	RegCloseKey(hkey);
}

double cvGetMode_W32(const char* name) { //YV
	double result = -1;

	CV_FUNCNAME( "cvGetMode_W32" );

	__BEGIN__;

	CvWindow* window;

	if (!name) {
		CV_ERROR( CV_StsNullPtr, "NULL name string" );
	}

	window = icvFindWindowByName( name );
	if ( !window ) {
		CV_ERROR( CV_StsNullPtr, "NULL window" );
	}

	result = window->status;

	__END__;
	return result;
}

#ifdef MONITOR_DEFAULTTONEAREST
void cvChangeMode_W32( const char* name, double prop_value) { //Yannick Verdie
	CV_FUNCNAME( "cvChangeMode_W32" );

	__BEGIN__;

	CvWindow* window;

	if (!name) {
		CV_ERROR( CV_StsNullPtr, "NULL name string" );
	}

	window = icvFindWindowByName( name );
	if ( !window ) {
		CV_ERROR( CV_StsNullPtr, "NULL window" );
	}

	if (window->flags & CV_WINDOW_AUTOSIZE) { //if the flag CV_WINDOW_AUTOSIZE is set
		EXIT;
	}

	{
		DWORD dwStyle = GetWindowLongPtr(window->frame, GWL_STYLE);
		CvRect position;

		if (window->status == CV_WINDOW_FULLSCREEN && prop_value == CV_WINDOW_NORMAL) {
			icvLoadWindowPos(window->name, position );
			SetWindowLongPtr(window->frame, GWL_STYLE, dwStyle | WS_CAPTION);

			SetWindowPos(window->frame, HWND_TOP, position.x, position.y , position.width, position.height, SWP_NOZORDER | SWP_FRAMECHANGED);
			window->status = CV_WINDOW_NORMAL;

			EXIT;
		}

		if (window->status == CV_WINDOW_NORMAL && prop_value == CV_WINDOW_FULLSCREEN) {
			//save dimension
			RECT rect;
			GetWindowRect(window->frame, &rect);
			CvRect RectCV = cvRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
			icvSaveWindowPos(window->name, RectCV );

			//Look at coordinate for fullscreen
			HMONITOR hMonitor;
			MONITORINFO mi;
			hMonitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST);

			mi.cbSize = sizeof(mi);
			GetMonitorInfo(hMonitor, &mi);

			//fullscreen
			position.x = mi.rcMonitor.left; position.y = mi.rcMonitor.top;
			position.width = mi.rcMonitor.right - mi.rcMonitor.left; position.height = mi.rcMonitor.bottom - mi.rcMonitor.top;
			SetWindowLongPtr(window->frame, GWL_STYLE, dwStyle & ~WS_CAPTION);

			SetWindowPos(window->frame, HWND_TOP, position.x, position.y , position.width, position.height, SWP_NOZORDER | SWP_FRAMECHANGED);
			window->status = CV_WINDOW_FULLSCREEN;

			EXIT;
		}
	}

	__END__;
}
#else
void cvChangeMode_W32( const char*, double) {
}
#endif

CV_IMPL int cvNamedWindow( const char* name, int flags ) {
	int result = 0;
	CV_FUNCNAME( "cvNamedWindow" );

	__BEGIN__;

	HWND hWnd, mainhWnd;
	CvWindow* window;
	DWORD defStyle = WS_VISIBLE | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU;
	int len;
	CvRect rect;

	cvInitSystem(0, 0);

	if ( !name ) {
		CV_ERROR( CV_StsNullPtr, "NULL name string" );
	}

	// Check the name in the storage
	if ( icvFindWindowByName( name ) != 0 ) {
		result = 1;
		EXIT;
	}

	if ( !(flags & CV_WINDOW_AUTOSIZE)) { //YV add border in order to resize the window
		defStyle |= WS_SIZEBOX;
	}

	icvLoadWindowPos( name, rect );

	mainhWnd = CreateWindow( "Main HighGUI class", name, defStyle | WS_OVERLAPPED,
							 rect.x, rect.y, rect.width, rect.height, 0, 0, hg_hinstance, 0 );
	if ( !mainhWnd ) {
		CV_ERROR( CV_StsError, "Frame window can not be created" );
	}

	ShowWindow(mainhWnd, SW_SHOW);

	//YV- remove one border by changing the style
	hWnd = CreateWindow("HighGUI class", "", (defStyle & ~WS_SIZEBOX) | WS_CHILD, CW_USEDEFAULT, 0, rect.width, rect.height, mainhWnd, 0, hg_hinstance, 0);
	if ( !hWnd ) {
		CV_ERROR( CV_StsError, "Frame window can not be created" );
	}

	ShowWindow(hWnd, SW_SHOW);

	len = (int)strlen(name);
	CV_CALL( window = (CvWindow*)cvAlloc(sizeof(CvWindow) + len + 1));

	window->signature = CV_WINDOW_MAGIC_VAL;
	window->hwnd = hWnd;
	window->frame = mainhWnd;
	window->name = (char*)(window + 1);
	memcpy( window->name, name, len + 1 );
	window->flags = flags;
	window->image = 0;
	window->dc = CreateCompatibleDC(0);
	window->last_key = 0;
	window->status = CV_WINDOW_NORMAL;//YV

	window->on_mouse = 0;
	window->on_mouse_param = 0;

	memset( &window->toolbar, 0, sizeof(window->toolbar));

	window->next = hg_windows;
	window->prev = 0;
	if ( hg_windows ) {
		hg_windows->prev = window;
	}
	hg_windows = window;
	icvSetWindowLongPtr( hWnd, CV_USERDATA, window );
	icvSetWindowLongPtr( mainhWnd, CV_USERDATA, window );

	// Recalculate window position
	icvUpdateWindowPos( window );

	result = 1;
	__END__;

	return result;
}


static void icvRemoveWindow( CvWindow* window ) {
	CvTrackbar* trackbar = NULL;
	RECT wrect = {0, 0, 0, 0};

	if ( window->frame ) {
		GetWindowRect( window->frame, &wrect );
	}
	if ( window->name )
		icvSaveWindowPos( window->name, cvRect(wrect.left, wrect.top,
											   wrect.right - wrect.left, wrect.bottom - wrect.top) );

	if ( window->hwnd ) {
		icvSetWindowLongPtr( window->hwnd, CV_USERDATA, 0 );
	}
	if ( window->frame ) {
		icvSetWindowLongPtr( window->frame, CV_USERDATA, 0 );
	}

	if ( window->toolbar.toolbar ) {
		icvSetWindowLongPtr(window->toolbar.toolbar, CV_USERDATA, 0);
	}

	if ( window->prev ) {
		window->prev->next = window->next;
	} else {
		hg_windows = window->next;
	}

	if ( window->next ) {
		window->next->prev = window->prev;
	}

	window->prev = window->next = 0;

	if ( window->dc && window->image ) {
		DeleteObject(SelectObject(window->dc, window->image));
	}

	if ( window->dc ) {
		DeleteDC(window->dc);
	}

	for ( trackbar = window->toolbar.first; trackbar != 0; ) {
		CvTrackbar* next = trackbar->next;
		if ( trackbar->hwnd ) {
			icvSetWindowLongPtr( trackbar->hwnd, CV_USERDATA, 0 );
			cvFree( &trackbar );
		}
		trackbar = next;
	}

	cvFree( &window );
}


CV_IMPL void cvDestroyWindow( const char* name ) {
	CV_FUNCNAME( "cvDestroyWindow" );

	__BEGIN__;

	CvWindow* window;
	HWND mainhWnd;

	if (!name) {
		CV_ERROR( CV_StsNullPtr, "NULL name string" );
	}

	window = icvFindWindowByName( name );
	if ( !window ) {
		EXIT;
	}

	mainhWnd = window->frame;

	SendMessage(window->hwnd, WM_CLOSE, 0, 0);
	SendMessage( mainhWnd, WM_CLOSE, 0, 0);
	// Do NOT call _remove_window -- CvWindow list will be updated automatically ...

	__END__;
}


static void icvScreenToClient( HWND hwnd, RECT* rect ) {
	POINT p;
	p.x = rect->left;
	p.y = rect->top;
	ScreenToClient(hwnd, &p);
	OffsetRect( rect, p.x - rect->left, p.y - rect->top );
}


/* Calculatess the window coordinates relative to the upper left corner of the mainhWnd window */
static RECT icvCalcWindowRect( CvWindow* window ) {
	const int gutter = 1;
	RECT crect, trect, rect;

	assert(window);

	GetClientRect(window->frame, &crect);
	if (window->toolbar.toolbar) {
		GetWindowRect(window->toolbar.toolbar, &trect);
		icvScreenToClient(window->frame, &trect);
		SubtractRect( &rect, &crect, &trect);
	} else {
		rect = crect;
	}

	rect.top += gutter;
	rect.left += gutter;
	rect.bottom -= gutter;
	rect.right -= gutter;

	return rect;
}

// returns TRUE if there is a problem such as ERROR_IO_PENDING.
static bool icvGetBitmapData( CvWindow* window, SIZE* size, int* channels, void** data ) {
	BITMAP bmp;
	GdiFlush();
	HGDIOBJ h = GetCurrentObject( window->dc, OBJ_BITMAP );
	if ( size ) {
		size->cx = size->cy = 0;
	}
	if ( data ) {
		*data = 0;
	}

	if (h == NULL) {
		return true;
	}
	if (GetObject(h, sizeof(bmp), &bmp) == 0) {
		return true;
	}

	if ( size ) {
		size->cx = abs(bmp.bmWidth);
		size->cy = abs(bmp.bmHeight);
	}

	if ( channels ) {
		*channels = bmp.bmBitsPixel / 8;
	}

	if ( data ) {
		*data = bmp.bmBits;
	}

	return false;
}


static void icvUpdateWindowPos( CvWindow* window ) {
	RECT rect;
	assert(window);

	if ( (window->flags & CV_WINDOW_AUTOSIZE) && window->image ) {
		int i;
		SIZE size = {0, 0};
		icvGetBitmapData( window, &size, 0, 0 );

		// Repeat two times because after the first resizing of the mainhWnd window
		// toolbar may resize too
		for (i = 0; i < (window->toolbar.toolbar ? 2 : 1); i++) {
			RECT rmw, rw = icvCalcWindowRect(window );
			MoveWindow(window->hwnd, rw.left, rw.top,
					   rw.right - rw.left + 1, rw.bottom - rw.top + 1, FALSE);
			GetClientRect(window->hwnd, &rw);
			GetWindowRect(window->frame, &rmw);
			// Resize the mainhWnd window in order to make the bitmap fit into the child window
			MoveWindow(window->frame, rmw.left, rmw.top,
					   rmw.right - rmw.left + size.cx - rw.right + rw.left,
					   rmw.bottom  - rmw.top + size.cy - rw.bottom + rw.top, TRUE );
		}
	}

	rect = icvCalcWindowRect(window);
	MoveWindow(window->hwnd, rect.left, rect.top,
			   rect.right - rect.left + 1,
			   rect.bottom - rect.top + 1, TRUE );
}


CV_IMPL void
cvShowImage( const char* name, const CvArr* arr ) {
	CV_FUNCNAME( "cvShowImage" );

	__BEGIN__;

	CvWindow* window;
	SIZE size = { 0, 0 };
	int channels = 0;
	void* dst_ptr = 0;
	const int channels0 = 3;
	int origin = 0;
	CvMat stub, dst, *image;
	bool changed_size = false; // philipg

	if ( !name ) {
		CV_ERROR( CV_StsNullPtr, "NULL name" );
	}

	window = icvFindWindowByName(name);
	if (!window) {
		cvNamedWindow(name, 1);
		window = icvFindWindowByName(name);
	}

	if ( !window || !arr ) {
		EXIT;    // keep silence here.
	}

	if ( CV_IS_IMAGE_HDR( arr )) {
		origin = ((IplImage*)arr)->origin;
	}

	CV_CALL( image = cvGetMat( arr, &stub ));

	if (window->image)
		// if there is something wrong with these system calls, we cannot display image...
		if (icvGetBitmapData( window, &size, &channels, &dst_ptr )) {
			return;
		}

	if ( size.cx != image->width || size.cy != image->height || channels != channels0 ) {
		changed_size = true;

		uchar buffer[sizeof(BITMAPINFO) + 255*sizeof(RGBQUAD)];
		BITMAPINFO* binfo = (BITMAPINFO*)buffer;

		DeleteObject( SelectObject( window->dc, window->image ));
		window->image = 0;

		size.cx = image->width;
		size.cy = image->height;
		channels = channels0;

		FillBitmapInfo( binfo, size.cx, size.cy, channels * 8, 1 );

		window->image = SelectObject( window->dc, CreateDIBSection(window->dc, binfo,
									  DIB_RGB_COLORS, &dst_ptr, 0, 0));
	}

	cvInitMatHeader( &dst, size.cy, size.cx, CV_8UC3,
					 dst_ptr, (size.cx * channels + 3) & -4 );
	cvConvertImage( image, &dst, origin == 0 ? CV_CVTIMG_FLIP : 0 );

	// ony resize window if needed
	if (changed_size) {
		icvUpdateWindowPos(window);
	}
	InvalidateRect(window->hwnd, 0, 0);
	// philipg: this is not needed and just slows things down
//    UpdateWindow(window->hwnd);

	__END__;
}


CV_IMPL void cvResizeWindow(const char* name, int width, int height ) {
	CV_FUNCNAME( "cvResizeWindow" );

	__BEGIN__;

	int i;
	CvWindow* window;
	RECT rmw, rw, rect;

	if ( !name ) {
		CV_ERROR( CV_StsNullPtr, "NULL name" );
	}

	window = icvFindWindowByName(name);
	if (!window) {
		EXIT;
	}

	// Repeat two times because after the first resizing of the mainhWnd window
	// toolbar may resize too
	for (i = 0; i < (window->toolbar.toolbar ? 2 : 1); i++) {
		rw = icvCalcWindowRect(window);
		MoveWindow(window->hwnd, rw.left, rw.top,
				   rw.right - rw.left + 1, rw.bottom - rw.top + 1, FALSE);
		GetClientRect(window->hwnd, &rw);
		GetWindowRect(window->frame, &rmw);
		// Resize the mainhWnd window in order to make the bitmap fit into the child window
		MoveWindow(window->frame, rmw.left, rmw.top,
				   rmw.right - rmw.left + width - rw.right + rw.left,
				   rmw.bottom  - rmw.top + height - rw.bottom + rw.top, TRUE);
	}

	rect = icvCalcWindowRect(window);
	MoveWindow(window->hwnd, rect.left, rect.top,
			   rect.right - rect.left + 1, rect.bottom - rect.top + 1, TRUE);

	__END__;
}


CV_IMPL void cvMoveWindow( const char* name, int x, int y ) {
	CV_FUNCNAME( "cvMoveWindow" );

	__BEGIN__;

	CvWindow* window;
	RECT rect;

	if ( !name ) {
		CV_ERROR( CV_StsNullPtr, "NULL name" );
	}

	window = icvFindWindowByName(name);
	if (!window) {
		EXIT;
	}

	GetWindowRect( window->frame, &rect );
	MoveWindow( window->frame, x, y, rect.right - rect.left, rect.bottom - rect.top, TRUE);

	__END__;
}


static LRESULT CALLBACK
MainWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
	CvWindow* window = icvWindowByHWND( hwnd );
	if ( !window ) {
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}

	switch (uMsg) {
	case WM_DESTROY:

		icvRemoveWindow(window);
		// Do nothing!!!
		//PostQuitMessage(0);
		break;

	case WM_GETMINMAXINFO:
		if ( !(window->flags & CV_WINDOW_AUTOSIZE) ) {
			MINMAXINFO* minmax = (MINMAXINFO*)lParam;
			RECT rect;
			LRESULT retval = DefWindowProc(hwnd, uMsg, wParam, lParam);

			minmax->ptMinTrackSize.y = 100;
			minmax->ptMinTrackSize.x = 100;

			if ( window->toolbar.first ) {
				GetWindowRect( window->toolbar.first->hwnd, &rect );
				minmax->ptMinTrackSize.y += window->toolbar.rows * (rect.bottom - rect.top);
				minmax->ptMinTrackSize.x = MAX(rect.right - rect.left + HG_BUDDY_WIDTH, HG_BUDDY_WIDTH * 2);
			}
			return retval;
		}
		break;

	case WM_WINDOWPOSCHANGED: {
		WINDOWPOS* pos = (WINDOWPOS*)lParam;

		// Update the toolbar position/size
		if (window->toolbar.toolbar) {
			RECT rect;
			GetWindowRect(window->toolbar.toolbar, &rect);
			MoveWindow(window->toolbar.toolbar, 0, 0, pos->cx, rect.bottom - rect.top, TRUE);
		}

		if (!(window->flags & CV_WINDOW_AUTOSIZE)) {
			icvUpdateWindowPos(window);
		}

		break;
	}

	case WM_ACTIVATE:
		if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) {
			SetFocus(window->hwnd);
		}
		break;

	case WM_ERASEBKGND: {
		RECT cr, tr, wrc;
		HRGN rgn, rgn1, rgn2;
		int ret;
		HDC hdc = (HDC)wParam;
		GetWindowRect(window->hwnd, &cr);
		icvScreenToClient(window->frame, &cr);
		if (window->toolbar.toolbar) {
			GetWindowRect(window->toolbar.toolbar, &tr);
			icvScreenToClient(window->frame, &tr);
		} else {
			tr.left = tr.top = tr.right = tr.bottom = 0;
		}

		GetClientRect(window->frame, &wrc);

		rgn = CreateRectRgn(0, 0, wrc.right, wrc.bottom);
		rgn1 = CreateRectRgn(cr.left, cr.top, cr.right, cr.bottom);
		rgn2 = CreateRectRgn(tr.left, tr.top, tr.right, tr.bottom);
		ret = CombineRgn(rgn, rgn, rgn1, RGN_DIFF);
		ret = CombineRgn(rgn, rgn, rgn2, RGN_DIFF);

		if (ret != NULLREGION && ret != ERROR) {
			FillRgn(hdc, rgn, (HBRUSH)icvGetClassLongPtr(hwnd, CV_HBRBACKGROUND));
		}

		DeleteObject(rgn);
		DeleteObject(rgn1);
		DeleteObject(rgn2);
	}
	return 1;
	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}


static LRESULT CALLBACK HighGUIProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
	CvWindow* window = icvWindowByHWND(hwnd);
	if ( !window )
		// This window is not mentioned in HighGUI storage
		// Actually, this should be error except for the case of calls to CreateWindow
	{
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}

	// Process the message
	switch (uMsg) {
	case WM_WINDOWPOSCHANGING: {
		LPWINDOWPOS pos = (LPWINDOWPOS)lParam;
		RECT rect = icvCalcWindowRect(window);
		pos->x = rect.left;
		pos->y = rect.top;
		pos->cx = rect.right - rect.left + 1;
		pos->cy = rect.bottom - rect.top + 1;
	}
	break;

	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
	case WM_MBUTTONDOWN:
	case WM_LBUTTONDBLCLK:
	case WM_RBUTTONDBLCLK:
	case WM_MBUTTONDBLCLK:
	case WM_LBUTTONUP:
	case WM_RBUTTONUP:
	case WM_MBUTTONUP:
	case WM_MOUSEMOVE:
		if ( window->on_mouse ) {
			POINT pt;
			RECT rect;
			SIZE size = {0, 0};

			int flags = (wParam & MK_LBUTTON ? CV_EVENT_FLAG_LBUTTON : 0) |
						(wParam & MK_RBUTTON ? CV_EVENT_FLAG_RBUTTON : 0) |
						(wParam & MK_MBUTTON ? CV_EVENT_FLAG_MBUTTON : 0) |
						(wParam & MK_CONTROL ? CV_EVENT_FLAG_CTRLKEY : 0) |
						(wParam & MK_SHIFT ? CV_EVENT_FLAG_SHIFTKEY : 0) |
						(GetKeyState(VK_MENU) < 0 ? CV_EVENT_FLAG_ALTKEY : 0);
			int event = uMsg == WM_LBUTTONDOWN ? CV_EVENT_LBUTTONDOWN :
						uMsg == WM_RBUTTONDOWN ? CV_EVENT_RBUTTONDOWN :
						uMsg == WM_MBUTTONDOWN ? CV_EVENT_MBUTTONDOWN :
						uMsg == WM_LBUTTONUP ? CV_EVENT_LBUTTONUP :
						uMsg == WM_RBUTTONUP ? CV_EVENT_RBUTTONUP :
						uMsg == WM_MBUTTONUP ? CV_EVENT_MBUTTONUP :
						uMsg == WM_LBUTTONDBLCLK ? CV_EVENT_LBUTTONDBLCLK :
						uMsg == WM_RBUTTONDBLCLK ? CV_EVENT_RBUTTONDBLCLK :
						uMsg == WM_MBUTTONDBLCLK ? CV_EVENT_MBUTTONDBLCLK :
						CV_EVENT_MOUSEMOVE;
			if ( uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN || uMsg == WM_MBUTTONDOWN ) {
				SetCapture( hwnd );
			}
			if ( uMsg == WM_LBUTTONUP || uMsg == WM_RBUTTONUP || uMsg == WM_MBUTTONUP ) {
				ReleaseCapture();
			}

			pt.x = LOWORD( lParam );
			pt.y = HIWORD( lParam );

			GetClientRect( window->hwnd, &rect );
			icvGetBitmapData( window, &size, 0, 0 );

			window->on_mouse( event, pt.x * size.cx / MAX(rect.right - rect.left, 1),
							  pt.y * size.cy / MAX(rect.bottom - rect.top, 1), flags,
							  window->on_mouse_param );
		}
		break;

	case WM_PAINT:
		if (window->image != 0) {
			int nchannels = 3;
			SIZE size = {0, 0};
			PAINTSTRUCT paint;
			HDC hdc;
			RGBQUAD table[256];

			// Determine the bitmap's dimensions
			icvGetBitmapData( window, &size, &nchannels, 0 );

			hdc = BeginPaint(hwnd, &paint);
			SetStretchBltMode(hdc, COLORONCOLOR);

			if ( nchannels == 1 ) {
				int i;
				for (i = 0; i < 256; i++) {
					table[i].rgbBlue = (unsigned char)i;
					table[i].rgbGreen = (unsigned char)i;
					table[i].rgbRed = (unsigned char)i;
				}
				SetDIBColorTable(window->dc, 0, 255, table);
			}

			if (window->flags & CV_WINDOW_AUTOSIZE) {
				BitBlt( hdc, 0, 0, size.cx, size.cy, window->dc, 0, 0, SRCCOPY );
			} else {
				RECT rect;
				GetClientRect(window->hwnd, &rect);
				StretchBlt( hdc, 0, 0, rect.right - rect.left, rect.bottom - rect.top,
							window->dc, 0, 0, size.cx, size.cy, SRCCOPY );
			}
			//DeleteDC(hdc);
			EndPaint(hwnd, &paint);
		} else {
			return DefWindowProc(hwnd, uMsg, wParam, lParam);
		}
		return 0;

	case WM_ERASEBKGND:
		if (window->image) {
			return 0;
		}
		break;

	case WM_DESTROY:

		icvRemoveWindow(window);
		// Do nothing!!!
		//PostQuitMessage(0);
		break;

	case WM_SETCURSOR:
		SetCursor((HCURSOR)icvGetClassLongPtr(hwnd, CV_HCURSOR));
		return 0;

	case WM_KEYDOWN:
		window->last_key = (int)wParam;
		return 0;
	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}


static LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
	LRESULT ret;

	if ( hg_on_preprocess ) {
		int was_processed = 0;
		int ret = hg_on_preprocess(hwnd, uMsg, wParam, lParam, &was_processed);
		if ( was_processed ) {
			return ret;
		}
	}
	ret = HighGUIProc(hwnd, uMsg, wParam, lParam);

	if (hg_on_postprocess) {
		int was_processed = 0;
		int ret = hg_on_postprocess(hwnd, uMsg, wParam, lParam, &was_processed);
		if ( was_processed ) {
			return ret;
		}
	}

	return ret;
}


static void icvUpdateTrackbar( CvTrackbar* trackbar, int pos ) {
	const int max_name_len = 10;
	const char* suffix = "";
	char pos_text[32];
	int name_len;

	if ( trackbar->data ) {
		*trackbar->data = pos;
	}

	if ( trackbar->pos != pos ) {
		trackbar->pos = pos;
		if ( trackbar->notify2 ) {
			trackbar->notify2(pos, trackbar->userdata);
		}
		if ( trackbar->notify ) {
			trackbar->notify(pos);
		}

		name_len = (int)strlen(trackbar->name);

		if ( name_len > max_name_len ) {
			int start_len = max_name_len * 2 / 3;
			int end_len = max_name_len - start_len - 2;
			memcpy( pos_text, trackbar->name, start_len );
			memcpy( pos_text + start_len, "...", 3 );
			memcpy( pos_text + start_len + 3, trackbar->name + name_len - end_len, end_len + 1 );
		} else {
			memcpy( pos_text, trackbar->name, name_len + 1);
		}

		sprintf( pos_text + strlen(pos_text), "%s: %d\n", suffix, pos );
		SetWindowText( trackbar->buddy, pos_text );
	}
}


static LRESULT CALLBACK HGToolbarProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
	CvWindow* window = icvWindowByHWND( hwnd );
	if (!window) {
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}

	// Control messages processing
	switch (uMsg) {
		// Slider processing
	case WM_HSCROLL: {
		HWND slider = (HWND)lParam;
		int pos = (int)SendMessage(slider, TBM_GETPOS, 0, 0);
		CvTrackbar* trackbar = icvTrackbarByHWND( slider );

		if ( trackbar ) {
			if ( trackbar->pos != pos ) {
				icvUpdateTrackbar( trackbar, pos );
			}
		}

		SetFocus( window->hwnd );
		return 0;
	}

	case WM_NCCALCSIZE: {
		LRESULT ret = CallWindowProc(window->toolbar.toolBarProc, hwnd, uMsg, wParam, lParam);
		int rows = (int)SendMessage(hwnd, TB_GETROWS, 0, 0);

		if (window->toolbar.rows != rows) {
			SendMessage(window->toolbar.toolbar, TB_BUTTONCOUNT, 0, 0);
			CvTrackbar* trackbar = window->toolbar.first;

			for ( ; trackbar != 0; trackbar = trackbar->next ) {
				RECT rect;
				SendMessage(window->toolbar.toolbar, TB_GETITEMRECT,
							(WPARAM)trackbar->id, (LPARAM)&rect);
				MoveWindow(trackbar->hwnd, rect.left + HG_BUDDY_WIDTH, rect.top,
						   rect.right - rect.left - HG_BUDDY_WIDTH,
						   rect.bottom - rect.top, FALSE);
				MoveWindow(trackbar->buddy, rect.left, rect.top,
						   HG_BUDDY_WIDTH, rect.bottom - rect.top, FALSE);
			}
			window->toolbar.rows = rows;
		}
		return ret;
	}
	}

	return CallWindowProc(window->toolbar.toolBarProc, hwnd, uMsg, wParam, lParam);
}


CV_IMPL void
cvDestroyAllWindows(void) {
	CvWindow* window = hg_windows;

	while ( window ) {
		HWND mainhWnd = window->frame;
		HWND hwnd = window->hwnd;
		window = window->next;

		SendMessage( hwnd, WM_CLOSE, 0, 0 );
		SendMessage( mainhWnd, WM_CLOSE, 0, 0 );
	}
}


CV_IMPL int
cvWaitKey( int delay ) {
	int time0 = GetTickCount();

	for (;;) {
		CvWindow* window;
		MSG message;
		int is_processed = 0;

		if ( (delay > 0 && abs((int)(GetTickCount() - time0)) >= delay) || hg_windows == 0 ) {
			return -1;
		}

		if ( delay <= 0 ) {
			GetMessage(&message, 0, 0, 0);
		} else if ( PeekMessage(&message, 0, 0, 0, PM_REMOVE) == FALSE ) {
			Sleep(1);
			continue;
		}

		for ( window = hg_windows; window != 0 && is_processed == 0; window = window->next ) {
			if ( window->hwnd == message.hwnd || window->frame == message.hwnd ) {
				is_processed = 1;
				switch (message.message) {
				case WM_DESTROY:
				case WM_CHAR:
					DispatchMessage(&message);
					return (int)message.wParam;

				case WM_SYSKEYDOWN:
					if ( message.wParam == VK_F10 ) {
						is_processed = 1;
						return (int)(message.wParam << 16);
					}
					break;

				case WM_KEYDOWN:
					TranslateMessage(&message);
					if ( (message.wParam >= VK_F1 && message.wParam <= VK_F24) ||
							message.wParam == VK_HOME || message.wParam == VK_END ||
							message.wParam == VK_UP || message.wParam == VK_DOWN ||
							message.wParam == VK_LEFT || message.wParam == VK_RIGHT ||
							message.wParam == VK_INSERT || message.wParam == VK_DELETE ||
							message.wParam == VK_PRIOR || message.wParam == VK_NEXT ) {
						DispatchMessage(&message);
						is_processed = 1;
						return (int)(message.wParam << 16);
					}
				default:
					DispatchMessage(&message);
					is_processed = 1;
					break;
				}
			}
		}

		if ( !is_processed ) {
			TranslateMessage(&message);
			DispatchMessage(&message);
		}
	}
}


static CvTrackbar*
icvFindTrackbarByName( const CvWindow* window, const char* name ) {
	CvTrackbar* trackbar = window->toolbar.first;

	for ( ; trackbar != 0 && strcmp( trackbar->name, name ) != 0; trackbar = trackbar->next )
		;

	return trackbar;
}


typedef struct {
	UINT cbSize;
	DWORD dwMask;
	int idCommand;
	int iImage;
	BYTE fsState;
	BYTE fsStyle;
	WORD cx;
	DWORD lParam;
	LPSTR pszText;
	int cchText;
}
ButtonInfo;


static int
icvCreateTrackbar( const char* trackbar_name, const char* window_name,
				   int* val, int count, CvTrackbarCallback on_notify,
				   CvTrackbarCallback2 on_notify2, void* userdata ) {
	int result = 0;

	CV_FUNCNAME( "icvCreateTrackbar" );

	__BEGIN__;

	char slider_name[32];
	CvWindow* window = 0;
	CvTrackbar* trackbar = 0;
	int pos = 0;

	if ( !window_name || !trackbar_name ) {
		CV_ERROR( CV_StsNullPtr, "NULL window or trackbar name" );
	}

	if ( count <= 0 ) {
		CV_ERROR( CV_StsOutOfRange, "Bad trackbar maximal value" );
	}

	window = icvFindWindowByName(window_name);
	if ( !window ) {
		EXIT;
	}

	trackbar = icvFindTrackbarByName(window, trackbar_name);
	if ( !trackbar ) {
		TBBUTTON tbs;
		ButtonInfo tbis;
		RECT rect;
		int bcount;
		int len = (int)strlen( trackbar_name );

		// create toolbar if it is not created yet
		if ( !window->toolbar.toolbar ) {
			const int default_height = 30;

			window->toolbar.toolbar = CreateToolbarEx(
										  window->frame, WS_CHILD | CCS_TOP | TBSTYLE_WRAPABLE,
										  1, 0, 0, 0, 0, 0, 16, 20, 16, 16, sizeof(TBBUTTON));
			GetClientRect(window->frame, &rect);
			MoveWindow( window->toolbar.toolbar, 0, 0,
						rect.right - rect.left, default_height, TRUE);
			SendMessage(window->toolbar.toolbar, TB_AUTOSIZE, 0, 0);
			ShowWindow(window->toolbar.toolbar, SW_SHOW);

			window->toolbar.first = 0;
			window->toolbar.pos = 0;
			window->toolbar.rows = 0;
			window->toolbar.toolBarProc =
				(WNDPROC)icvGetWindowLongPtr(window->toolbar.toolbar, CV_WNDPROC);

			icvUpdateWindowPos(window);

			// Subclassing from toolbar
			icvSetWindowLongPtr(window->toolbar.toolbar, CV_WNDPROC, HGToolbarProc);
			icvSetWindowLongPtr(window->toolbar.toolbar, CV_USERDATA, window);
		}

		/* Retrieve current buttons count */
		bcount = (int)SendMessage(window->toolbar.toolbar, TB_BUTTONCOUNT, 0, 0);

		if (bcount > 1) {
			/* If this is not the first button then we need to
			separate it from the previous one */
			tbs.iBitmap = 0;
			tbs.idCommand = bcount; // Set button id to it's number
			tbs.iString = 0;
			tbs.fsStyle = TBSTYLE_SEP;
			tbs.fsState = TBSTATE_ENABLED;
			SendMessage(window->toolbar.toolbar, TB_ADDBUTTONS, 1, (LPARAM)&tbs);

			// Retrieve current buttons count
			bcount = (int)SendMessage(window->toolbar.toolbar, TB_BUTTONCOUNT, 0, 0);
		}

		/* Add a button which we're going to cover with the slider */
		tbs.iBitmap = 0;
		tbs.idCommand = bcount; // Set button id to it's number
		tbs.fsState = TBSTATE_ENABLED;
#if 0/*!defined WIN64 && !defined EM64T*/
		tbs.fsStyle = 0;
		tbs.iString = 0;
#else

#ifndef TBSTYLE_AUTOSIZE
#define TBSTYLE_AUTOSIZE        0x0010
#endif

#ifndef TBSTYLE_GROUP
#define TBSTYLE_GROUP           0x0004
#endif
		//tbs.fsStyle = TBSTYLE_AUTOSIZE;
		tbs.fsStyle = TBSTYLE_GROUP;
		tbs.iString = (INT_PTR)trackbar_text;
#endif
		SendMessage(window->toolbar.toolbar, TB_ADDBUTTONS, 1, (LPARAM)&tbs);

		/* Adjust button size to the slider */
		tbis.cbSize = sizeof(tbis);
		tbis.dwMask = TBIF_SIZE;

		GetClientRect(window->hwnd, &rect);
		tbis.cx = (unsigned short)(rect.right - rect.left);

		SendMessage(window->toolbar.toolbar, TB_SETBUTTONINFO,
					(WPARAM)tbs.idCommand, (LPARAM)&tbis);

		/* Get button position */
		SendMessage(window->toolbar.toolbar, TB_GETITEMRECT,
					(WPARAM)tbs.idCommand, (LPARAM)&rect);

		/* Create a slider */
		trackbar = (CvTrackbar*)cvAlloc( sizeof(CvTrackbar) + len + 1 );
		trackbar->signature = CV_TRACKBAR_MAGIC_VAL;
		trackbar->notify = 0;
		trackbar->notify2 = 0;
		trackbar->parent = window;
		trackbar->pos = 0;
		trackbar->data = 0;
		trackbar->id = bcount;
		trackbar->next = window->toolbar.first;
		trackbar->name = (char*)(trackbar + 1);
		memcpy( trackbar->name, trackbar_name, len + 1 );
		window->toolbar.first = trackbar;

		sprintf(slider_name, "Trackbar%p", val);
		trackbar->hwnd = CreateWindowEx(0, TRACKBAR_CLASS, slider_name,
										WS_CHILD | WS_VISIBLE | TBS_AUTOTICKS |
										TBS_FIXEDLENGTH | TBS_HORZ | TBS_BOTTOM,
										rect.left + HG_BUDDY_WIDTH, rect.top,
										rect.right - rect.left - HG_BUDDY_WIDTH,
										rect.bottom - rect.top, window->toolbar.toolbar,
										(HMENU)(size_t)bcount, hg_hinstance, 0);

		sprintf(slider_name, "Buddy%p", val);
		trackbar->buddy = CreateWindowEx(0, "STATIC", slider_name,
										 WS_CHILD | SS_RIGHT,
										 rect.left, rect.top,
										 HG_BUDDY_WIDTH, rect.bottom - rect.top,
										 window->toolbar.toolbar, 0, hg_hinstance, 0);

		icvSetWindowLongPtr( trackbar->hwnd, CV_USERDATA, trackbar );

		/* Minimize the number of rows */
		SendMessage( window->toolbar.toolbar, TB_SETROWS,
					 MAKEWPARAM(1, FALSE), (LPARAM)&rect );
	} else {
		trackbar->data = 0;
		trackbar->notify = 0;
		trackbar->notify2 = 0;
	}

	trackbar->maxval = count;

	/* Adjust slider parameters */
	SendMessage(trackbar->hwnd, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(0, count));
	SendMessage(trackbar->hwnd, TBM_SETTICFREQ, (WPARAM)1, (LPARAM)0 );
	if ( val ) {
		pos = *val;
	}

	SendMessage(trackbar->hwnd, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)pos );
	SendMessage(window->toolbar.toolbar, TB_AUTOSIZE, 0, 0);

	trackbar->pos = -1;
	icvUpdateTrackbar( trackbar, pos );
	ShowWindow( trackbar->buddy, SW_SHOW );
	ShowWindow( trackbar->hwnd, SW_SHOW );

	trackbar->notify = on_notify;
	trackbar->notify2 = on_notify2;
	trackbar->userdata = userdata;
	trackbar->data = val;

	/* Resize the window to reflect the toolbar resizing*/
	icvUpdateWindowPos(window);

	result = 1;

	__END__;

	return result;
}

CV_IMPL int
cvCreateTrackbar( const char* trackbar_name, const char* window_name,
				  int* val, int count, CvTrackbarCallback on_notify ) {
	return icvCreateTrackbar( trackbar_name, window_name, val, count,
							  on_notify, 0, 0 );
}

CV_IMPL int
cvCreateTrackbar2( const char* trackbar_name, const char* window_name,
				   int* val, int count, CvTrackbarCallback2 on_notify2,
				   void* userdata ) {
	return icvCreateTrackbar( trackbar_name, window_name, val, count,
							  0, on_notify2, userdata );
}

CV_IMPL void
cvSetMouseCallback( const char* window_name, CvMouseCallback on_mouse, void* param ) {
	CV_FUNCNAME( "cvSetMouseCallback" );

	__BEGIN__;

	CvWindow* window = 0;

	if ( !window_name ) {
		CV_ERROR( CV_StsNullPtr, "NULL window name" );
	}

	window = icvFindWindowByName(window_name);
	if ( !window ) {
		EXIT;
	}

	window->on_mouse = on_mouse;
	window->on_mouse_param = param;

	__END__;
}


CV_IMPL int cvGetTrackbarPos( const char* trackbar_name, const char* window_name ) {
	int pos = -1;

	CV_FUNCNAME( "cvGetTrackbarPos" );

	__BEGIN__;

	CvWindow* window;
	CvTrackbar* trackbar = 0;

	if ( trackbar_name == 0 || window_name == 0 ) {
		CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" );
	}

	window = icvFindWindowByName( window_name );
	if ( window ) {
		trackbar = icvFindTrackbarByName( window, trackbar_name );
	}

	if ( trackbar ) {
		pos = trackbar->pos;
	}

	__END__;

	return pos;
}


CV_IMPL void cvSetTrackbarPos( const char* trackbar_name, const char* window_name, int pos ) {
	CV_FUNCNAME( "cvSetTrackbarPos" );

	__BEGIN__;

	CvWindow* window;
	CvTrackbar* trackbar = 0;

	if ( trackbar_name == 0 || window_name == 0 ) {
		CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" );
	}

	window = icvFindWindowByName( window_name );
	if ( window ) {
		trackbar = icvFindTrackbarByName( window, trackbar_name );
	}

	if ( trackbar ) {
		if ( pos < 0 ) {
			pos = 0;
		}

		if ( pos > trackbar->maxval ) {
			pos = trackbar->maxval;
		}

		SendMessage( trackbar->hwnd, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)pos );
		icvUpdateTrackbar( trackbar, pos );
	}

	__END__;
}


CV_IMPL void* cvGetWindowHandle( const char* window_name ) {
	void* hwnd = 0;

	CV_FUNCNAME( "cvGetWindowHandle" );

	__BEGIN__;

	CvWindow* window;

	if ( window_name == 0 ) {
		CV_ERROR( CV_StsNullPtr, "NULL window name" );
	}

	window = icvFindWindowByName( window_name );
	if ( window ) {
		hwnd = (void*)window->hwnd;
	}

	__END__;

	return hwnd;
}


CV_IMPL const char* cvGetWindowName( void* window_handle ) {
	const char* window_name = "";

	CV_FUNCNAME( "cvGetWindowName" );

	__BEGIN__;

	CvWindow* window;

	if ( window_handle == 0 ) {
		CV_ERROR( CV_StsNullPtr, "NULL window" );
	}

	window = icvWindowByHWND( (HWND)window_handle );
	if ( window ) {
		window_name = window->name;
	}

	__END__;

	return window_name;
}



CV_IMPL void
cvSetPreprocessFuncWin32(int (__cdecl* on_preprocess)(HWND, UINT, WPARAM, LPARAM, int*)) {
	if (on_preprocess) {
		hg_on_preprocess = on_preprocess;
	} else {
		assert(on_preprocess);
	}
}

CV_IMPL void
cvSetPostprocessFuncWin32(int (__cdecl* on_postprocess)(HWND, UINT, WPARAM, LPARAM, int*)) {
	if (on_postprocess) {
		hg_on_postprocess = on_postprocess;
	} else {
		assert(on_postprocess);
	}
}

#endif //WIN32
